package com.example.score.score;

import com.example.score.account.Account;
import com.example.score.account.Manager;
import com.example.score.account.Referee;
import com.example.score.controller.TempController;
import com.example.score.entity.Message;
import com.example.score.entity.SportData;
import com.example.score.entity.Sportsman;
import com.example.score.entity.ViewMode;
import com.example.score.utils.JsonSocketMessageUtil;
import com.example.score.utils.ThreadManager;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.WebSocketMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import javax.annotation.Resource;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author fsyj
 */
@Slf4j
@Service
public class SocketScore extends TextWebSocketHandler {

    @Autowired
    Manager manager;

    @Resource
    JudgeSocket judgeSocket;

    @Resource
    ViewSocket viewSocket;

    @Resource
    TempController tempController;

    Map<WebSocketSession, Referee> sessionRefereeMap = new ConcurrentHashMap<>(2);

    Map<Referee, WebSocketSession> refereeSessionMap = new ConcurrentHashMap<>(2);

    Map<Account, Message> currentMessageMap = new HashMap<>(2);

    /**
     * 查看sessionRefereeMap中是否有当前session的键值对，如果没有，则初始化
     *
     * @param session
     * @throws Exception
     */
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        String refereeId = (String) session.getAttributes().get("refereeId");
        Referee referee = manager.getRefereeById(refereeId);
        if (refereeSessionMap.containsKey(referee)) {
            WebSocketSession lastSession = refereeSessionMap.get(referee);
            if (!lastSession.equals(session)) {
                try {
                    refereeSessionMap.put(referee, session);
                    // 删除上次的session
                    sessionRefereeMap.remove(lastSession);
                    // 更新
                    sessionRefereeMap.put(session, referee);
                    lastSession.close();
                } catch (IOException e) {
                    log.error("关闭连接{}出错：{}", lastSession, e.getMessage());
                }
            }
        } else {
            // 如果不包含key，则新增session
            refereeSessionMap.put(referee, session);
            sessionRefereeMap.put(session, referee);
        }
        log.info("裁判长：{}连接系统", referee.getAccount());
        log.info("当前在场裁判{}--{}", Arrays.toString(refereeSessionMap.keySet().toArray()), Arrays.toString(sessionRefereeMap.keySet().toArray()));
    }

    @Override
    public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) {
        Message data = JsonSocketMessageUtil.parseMessageToJson(message);
        // 利用反射动态调用方法
        try {
            Method method = this.getClass().getDeclaredMethod(data.getAction(), WebSocketSession.class, Message.class);
            method.invoke(this, session, data);
        } catch (Exception e) {
            log.error("方法{}调用出错", e.getMessage());
        }
    }

    public void ping(WebSocketSession session, Message message) throws IOException {
        session.sendMessage(JsonSocketMessageUtil.wrapMessage(message));
    }


    /**
     * 通知当前session所属的所有裁判进行打分
     *
     * @param session
     * @param message
     */
    public void notifyAllJudge(WebSocketSession session, Message message) throws IOException {
        // 将当前message与referee联系保存
        Referee referee = sessionRefereeMap.get(session);
        currentMessageMap.put(referee, message);
        judgeSocket.notifyJudgesScore(referee.getJudges(), message);
    }

    public void rescore(WebSocketSession session, Message message) throws IOException {
        List<SportData> dataList = message.getData();
        for (SportData sportData : dataList) {
            String judgeId = sportData.getJudgeId();
            log.info("裁判长{}通知裁判{}重打分", judgeId, message);
            List<Account> list = new ArrayList<>(1);
            list.add(manager.getJudgeById(judgeId));
            judgeSocket.notifyJudgesReScore(list, message);
        }
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        System.out.println("close" + sessionRefereeMap.get(session));
        Referee referee = sessionRefereeMap.get(session);
        sessionRefereeMap.remove(session);
//        refereeSessionMap.remove(referee);
//        currentMessageMap.remove(referee);
        log.info("裁判长{}退出系统", referee.getAccount());
        log.info("当前在场裁判{}--{}", Arrays.toString(refereeSessionMap.keySet().toArray()), Arrays.toString(sessionRefereeMap.keySet().toArray()));
    }

    /**
     * 开始比赛
     *
     * @param session
     * @param message
     */
    public void startContest(WebSocketSession session, Message message) throws IOException {
        // 获取裁判长ID
        Referee referee = sessionRefereeMap.get(session);
        ViewMode viewMessage = new ViewMode("0", message.getData());

        viewSocket.show(viewMessage, referee.getId());
        message.action = "score";
        notifyAllJudge(session, message);
    }


    /**
     * 展示下一个将要上场的运动员
     *
     * @param session
     * @param message
     */
    public void showScore(WebSocketSession session, Message message) {
        List<SportData> dataList = message.getData();
        ThreadManager.threadPool.submit(() -> {
            for (SportData data : dataList) {
                int length = tempController.getGroupLength(data);
                if (length < 8) {
                    length = 8;
                }
                try {
                    Thread.sleep(10000L + (((length / 8) + 1) * 5000L));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // 获取裁判长ID
                Referee referee = sessionRefereeMap.get(session);
                ViewMode viewMessage = new ViewMode("0", data);

                viewSocket.show(viewMessage, referee.getId());
            }
        });
    }


    /**
     * 向裁判长返回成绩
     *
     * @param referee
     * @param data
     */
    public void backScore(Account referee, Message data) {
        // 找到该裁判长对应的session
        WebSocketSession session = null;
        for (Referee referee1 : refereeSessionMap.keySet()) {
            if (referee1.getId().equals(referee.getId())) {
                session = refereeSessionMap.get(referee1);
                break;
            }
        }
        data.setAction("backScore");
        if (session != null) {
            try {
                session.sendMessage(JsonSocketMessageUtil.wrapMessage(data));
            } catch (Exception e) {
                log.error("系统裁判长 session 错误:" + e.getMessage());
            }
        }
    }

    public Message getCurrentMessage(Account referee) {
        if (referee == null) {
            return null;
        } else {
            return currentMessageMap.get(referee);
        }
    }

    public void confirm(String refereeId) {
        Referee referee = manager.getRefereeById(refereeId);
        WebSocketSession session = refereeSessionMap.get(referee);
        try {
            session.sendMessage(JsonSocketMessageUtil.wrapMessage(new Message("confirm", Collections.singletonList(new SportData()))));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void rollback(String refereeId) {
        Referee referee = manager.getRefereeById(refereeId);
        WebSocketSession session = refereeSessionMap.get(referee);
        try {
            session.sendMessage(JsonSocketMessageUtil.wrapMessage(new Message("rollback", Collections.singletonList(new SportData()))));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void startGroup(List<Sportsman> sportsmen, String refereeId) throws IOException {
        Referee referee = manager.getRefereeById(refereeId);
        WebSocketSession session = refereeSessionMap.get(referee);
        Message message = new Message();
        ArrayList<SportData> datas = new ArrayList<>(sportsmen.size());
        for (Sportsman sportsman : sportsmen) {
            SportData data = new SportData();
            data.setEvent(sportsman.getEvent());
            data.setGoal(String.valueOf(sportsman.getGoal()));
            data.setIndex(String.valueOf(sportsman.getIndex()));
            data.setGroupIndex(String.valueOf(sportsman.getGroupIndex()));
            data.setGroupName(sportsman.getGroupName());
            data.setName(sportsman.getSportName());
            data.setRefereeId(refereeId);
            data.setUuid(sportsman.getUuid());
            data.setUnit(sportsman.getUnit());
            datas.add(data);
        }
        message.setData(datas);
        notifyAllJudge(session, message);
    }
}
